Skip to content

Stop creating launch, compile, and KSP tasks at configuration time - #99

Merged
alexander-yevsyukov merged 14 commits into
masterfrom
claude/lazy-task-wiring-a189e2
Oct 30, 2026
Merged

alexander-yevsyukov merged 14 commits into
masterfrom
claude/lazy-task-wiring-a189e2

Conversation

@alexander-yevsyukov

@alexander-yevsyukov alexander-yevsyukov commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Closes #98. The Compiler Gradle plugin no longer creates the launch*SpineCompiler, compile*Java, compile*Kotlin, and ksp*Kotlin tasks by name while the projects are configured. The generate*Proto call site stays eager by design; see below.

What changed

Call site in #98 Now
handleLaunchTaskDependency() → TaskLocator.find() Removed. Each launch task depends on its own generate*Proto task by provider, from its registration action (LaunchSpineCompiler.setDependencies()).
LaunchSpineCompiler.setDependencies() → tool-base find*CompileFor() compile*Java, compile*Kotlin, and ksp*Kotlin depend on the launch task via tasks.named { … }.configureEach { dependsOn(launchTask) }, right after the launch task is registered. tool-base#198 is not needed.
arrangeKspTaskDependency() (afterEvaluate) Removed. The same name match covers a KSP task registered at any time.
configureGenerateProtoTasks() Still eager, see below. Its action no longer touches the launch task.
  • Launch and clean tasks are registered via sourceSets.configureEach. This replaces the eager forEach and the afterEvaluate fallback for source sets added later.
  • TaskLocator (public, gradle-api) gains exists() and named(), which do not create the task. find() and get() are unchanged.

Why the issue's suggested fix could not be used as is

Gradle (9.8.2) forbids configuring other tasks from inside a lazy task configuration action. named(…).configure {}, configureEach {}, and afterEvaluate {} all fail with "… cannot be executed in the current context". So the generateProto action cannot wire the launch task lazily. The wiring now goes the other way: the launch task pulls its producer by provider.

Why generate*Proto stays eager

With generateProtoTasks.all().configureEach {}, the tests/ integration build failed with ConcurrentModificationException:

  1. protobuf-gradle-plugin runs postConfigure right after the generateProtoTasks {} blocks.
  2. With idea applied, postConfigure calls Utils.addToIdeSources(), which reads sourceSets.main.java.srcDirs.
  3. Reading them resolves the generateProto output provider, which creates the task.
  4. Our action then calls tool-base configureSourceSetDirs(), which runs setSrcDirs() on the collection being iterated.

That is the "configuration order" the old comment guarded. The comment now says so, and TaskWiringSpec has a regression test with idea applied.

Keeping this call site eager is accepted as the resolution of #98 for generate*Proto:

  • Upstream: protobuf-gradle-plugin 0.10.0 itself calls generateProtoTask.get() in postConfigure whenever idea or eclipse is applied. In such builds these tasks are created during configuration whatever the Compiler does.
  • Without those plugins: tool-base ProtobufSetupPlugin creates them at the same stage. Making them lazy would first require tool-base to rewrite the source-set directories per source set, outside the task configuration.
  • Cost: one or two tasks per project (generateProto, generateTestProto).

Measured (./gradlew help, published .081 vs this branch)

  • ksp-test functional fixture (real Kotlin + KSP plugins):
    • Before: 14 of 53 tasks created. After: 6 of 53.
    • Gone: launch{,Test}SpineCompiler, compile{,Test}Java, compile{,Test}Kotlin, ksp{,Test}Kotlin.
    • Left: generate{,Test}Proto (eager on purpose), process{,Test}Resources (tool-base#198), help, clean.
  • This repo's tests/ build: the total is unchanged (169 of 219).
    • The 11 tasks this plugin used to create first are now created first by buildSrc's DokkaExts.disableDocumentationTasks (config#820).

Dependency and config updates

Later commits move the build to the releases that came out of the same effort in the other repositories.

  • config, pulled twice (363fc1da, then ba47b5fb):
  • protoc-plugin POM: config#821 now declares the jar packaging for standard publications. Writing the POM fails if the publication has no main JAR.
    • The jar task of protoc-plugin has the exe classifier, so the module sets the pom packaging itself.
    • This is the packaging Gradle derived for it before; the published .081 and .082 POMs have it too.
  • ToolBase .428 → .440, both version and dogfoodingVersion. .440 publishes jvm-tool-plugins and protobuf-setup-plugins as plain JARs (Publish protobuf-setup-plugins and jvm-tool-plugins as plain JARs tool-base#201).
  • CoreJvm Compiler .099 → .101, both version and dogfoodingVersion. .101 no longer bundles ToolBase classes in core-jvm-plugins (Stop bundling ToolBase classes in core-jvm-plugins core-jvm-compiler#125).
    • Before, in a project applying io.spine.core-jvm, the fat JAR's ToolBase .428 classes shadowed the ToolBase the Compiler plugin declares.
    • Now every project of the tests/ build loads them from plugin-base-2.0.0-SNAPSHOT.440.jar.
  • The Compiler still matches the compile task names itself instead of calling javaCompileTasksFor() and kotlinCompileTasksFor(), which ToolBase .430 added.
    • With CoreJvm Compiler .099 or older on the classpath, these calls fail with NoSuchMethodError.
    • The name matching works with any version.

For reviewers

  • Changed objects in dependsOn:
    • The compile and KSP tasks now hold a TaskProvider<LaunchSpineCompiler>, not the task.
    • The launch task holds a TaskProvider<GenerateProtoTask>, not the task.
    • Code that filters dependsOn for Task instances will not see these edges. ProjectConfigSpec now resolves taskDependencies instead. No Spine sibling repo does such filtering.
  • Registration-order assumption: the launch task finds its generate*Proto task by name when Gradle creates it. This relies on protobuf-gradle-plugin being applied first, which protobufExtensionOrFail() enforces. That plugin registers the task in its own earlier sourceSets.configureEach. A test covers a build that creates each task as soon as it is registered.
  • Separate commit, ExcludeProtocOutputSpec: the spec passed with the protoc filter disabled.
    • Its directory was added before evaluation, so the plugin had already removed it from the source set.
    • On macOS, the /var → /private/var symlink made the asserted path differ from the reported one.
    • The spec now fails without the filter.
  • Headers of touched files were updated by the repo's update-copyright hook.

Testing

  • ./gradlew build dokkaGenerate passes: unit, functional, and integration tests, detekt, and Dokka. It passes again after each dependency update above, the last time with ToolBase .440 and CoreJvm Compiler .101.
  • New TaskWiringSpec (12 cases) verifies that the dependencies hold and that the tasks are not created at configuration time.
    • Against the old code, the "not created", late-KSP, and late-source-set cases fail.
    • The idea case fails against the configureEach variant.
  • TaskLocatorSpec has 3 new cases.

🤖 Generated with Claude Code

alexander-yevsyukov and others added 5 commits October 29, 2026 18:18
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Stop the Compiler Gradle plugin from creating the `launch*SpineCompiler`,
`compile*Java`, `compile*Kotlin`, and `ksp*Kotlin` tasks by name while the
projects are configured (#98).

- Register the launch and clean tasks via `sourceSets.configureEach`, which
  also serves source sets added later, instead of an `afterEvaluate` fallback.
- Make the Java, Kotlin, and KSP tasks depend on the launch task via
  `tasks.named { … }.configureEach`, which also covers tasks registered later.
- Let the launch task depend on its `generate*Proto` task by provider.
  Gradle forbids configuring other tasks from a lazy configuration action,
  so the `GenerateProtoTask` action no longer touches the launch task.
- Add `TaskLocator.exists()` and `named()`, which create no tasks.

`generate*Proto` tasks are still configured eagerly: configuring them rewrites
the source set directories, which must happen before the Protobuf Gradle Plugin
passes them to the IDEA plugin. A lazy action fails that with
`ConcurrentModificationException`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The spec passed with the `protoc` output filter of `compileJava` disabled:

- The directory was added to the source set before the evaluation, and
  the plugin removes it from the source set then, so the task filter had
  nothing to filter. The directory is now re-added after the evaluation.
- The leaked directory was added via the `@TempDir` path, which goes through
  the `/var` symbolic link on macOS, so the file was reported under a path
  that differs from the asserted one. The paths now come from
  `project.projectDir`.

The assertions inspect the `files` of the task source, with a positive control
for a hand-written file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Cover a build that makes Gradle create each task as soon as it is
  registered, and document the registration order the launch task relies on
  to find its `generate*Proto` task.
- Make `consumeProtoFrom()` private and look the task up with the reified
  `named<GenerateProtoTask>()`.
- Pass the source set first to both helpers of `createTasks()`.
- Fix the wording of the KDoc and comments, including the `find()` return
  description, and document `UnknownTaskException` thrown by `get()`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 29, 2026 18:31
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-29T18:33:19.737824Z f2b063f PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approved

No active review issues are recorded for the lazy task-wiring changes.

No open issues 🎉

What changed in this PR

Addresses part of #98 by reducing configuration-time task creation in the Compiler Gradle plugin while preserving task dependencies. Protobuf generation tasks intentionally remain eager to avoid the documented IDEA integration failure.

Changes:

  • Uses task providers and lazy configuration to connect launch, compilation, KSP, and clean tasks, including late-added tasks and source sets.
  • Adds non-realizing TaskLocator APIs and regression tests for task wiring, configuration order, and source filtering.
  • Advances the compiler version to 2.0.0-SNAPSHOT.082 and refreshes dependency reports.
File Description
version.gradle.kts Advances the compiler snapshot version.
gradle-plugin/​src/​test/​kotlin/​io/​spine/​tools/​compiler/​gradle/​plugin/​TaskWiringSpec.kt Adds lazy task wiring and configuration-order regression tests.
gradle-plugin/​src/​test/​kotlin/​io/​spine/​tools/​compiler/​gradle/​plugin/​ProjectConfigSpec.kt Resolves task dependencies to recognize provider-backed edges.
gradle-plugin/​src/​test/​kotlin/​io/​spine/​tools/​compiler/​gradle/​plugin/​ExcludeProtocOutputSpec.kt Strengthens source-filter assertions and fixes path handling.
gradle-plugin/​src/​main/​kotlin/​io/​spine/​tools/​compiler/​gradle/​plugin/​Plugin.kt Registers and wires tasks lazily while retaining eager Protobuf configuration.
gradle-plugin/​src/​main/​kotlin/​io/​spine/​tools/​compiler/​gradle/​plugin/​LaunchSpineCompiler.kt Consumes Protobuf task dependencies and source directories through providers.
gradle-api/​src/​test/​kotlin/​io/​spine/​tools/​compiler/​gradle/​api/​TaskLocatorSpec.kt Tests non-realizing task lookup and missing-task behavior.
gradle-api/​src/​main/​kotlin/​io/​spine/​tools/​compiler/​gradle/​api/​TaskLocator.kt Adds exists() and named() APIs with lifecycle documentation.
docs/​dependencies/​pom.xml Updates the dependency-report project version.
docs/​dependencies/​dependencies.md Refreshes report versions and generation timestamps.
.agents/​tasks/​lazy-task-wiring.md Records the implementation plan and reported verification results.

Review effort: Balanced

alexander-yevsyukov and others added 4 commits October 29, 2026 19:50
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Restore ToolBase `2.0.0-SNAPSHOT.430`, which `./config/pull` reverted to
`.428`. The Compiler does not call the lazy compile-task API added in `.430`:
the CoreJvm Compiler fat JAR on the build-script classpath of consumers
bundles an older ToolBase, which would fail such calls with `NoSuchMethodError`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The `jar` task of `protoc-plugin` has the `exe` classifier, so its publication
has no main artifact, and Gradle has derived the `pom` packaging for its POM.
`spinePublishing` now declares the `jar` packaging for standard publications
and fails writing a POM whose publication has no main JAR (config#821).
Restore the packaging published so far.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
alexander-yevsyukov and others added 5 commits October 30, 2026 16:32
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Move ToolBase and its dogfooding version to `2.0.0-SNAPSHOT.440`, the first
version that publishes `jvm-tool-plugins` and `protobuf-setup-plugins` as plain
JARs (SpineEventEngine/tool-base#201). `./config/pull` had reverted `version`
to `.428`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Move CoreJvm Compiler and its dogfooding version to `2.0.0-SNAPSHOT.101`,
which no longer bundles ToolBase classes in `core-jvm-plugins`
(SpineEventEngine/core-jvm-compiler#125). The ToolBase classes on the build
classpath of projects applying `io.spine.core-jvm` now come from the declared
ToolBase `.440` artifacts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov

codecov Bot commented Oct 30, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.44444% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 79.92%. Comparing base (99e65b2) to head (12bda85).

Additional details and impacted files
@@             Coverage Diff              @@
##             master      #99      +/-   ##
============================================
+ Coverage     78.27%   79.92%   +1.65%     
- Complexity      707      709       +2     
============================================
  Files           207      207              
  Lines          4648     4632      -16     
  Branches        407      400       -7     
============================================
+ Hits           3638     3702      +64     
+ Misses          879      802      -77     
+ Partials        131      128       -3     
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@alexander-yevsyukov
alexander-yevsyukov merged commit 5c6b0c2 into master Oct 30, 2026
11 checks passed
@alexander-yevsyukov
alexander-yevsyukov deleted the claude/lazy-task-wiring-a189e2 branch October 30, 2026 17:21
@github-project-automation github-project-automation Bot moved this from 🏗 In progress to ✅ Done in v2.0 Oct 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

Stop creating tasks eagerly in the Compiler Gradle plugin

2 participants